package restless.core {
	import com.greensock.motionPaths.RectanglePath2D;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import restless.core.interfaces.I2d;
	import restless.core.gameObjs.RLEManagedObj;
	import restless.utilities.numeric.RLERandom;
	
	/**
	 * 
	 * @author Jordan Laine
	 * 
	 * 
	 */
	
	public class RLECamera extends RLEManagedObj {
		
		private var bObjOnCamera:Boolean = false;
		private var bParallax:Boolean = false;
		private var bMove:Boolean = true;
		private var bUpdateFromObj:Boolean = false;
		private var bUpdateFromMouse:Boolean = false;
		private var bShake:Boolean = false;
		private var bOffset:Boolean = false;
		private var bMovementBounds:Boolean = false;
		
		private var nShakeAmnt:int = 0;
		private var shakeX:int = 0;
		private var shakeY:int = 0;
		private var nLastShake:int = 0;
		
		private var _parallax:RLEParallax;
		private var tempObj:I2d;
		private var updateObj:I2d;
		private var updateRect:Rectangle;
		private var offsetPoint:Point;
		private var gameWorldRect:Rectangle;		
		private var movementBoundsRect:Rectangle;
		private var speedPoint:Point;
		private var indexOfObj:int;
		private var layerNum:int;
		private var aRenderLayers:Array;
		private var cameraIdleRect:Rectangle;
		private var _movePoint:Point = new Point(0,0);
		
		public function RLECamera(x:Number, y:Number, width:Number, height:Number) {
			setRE();
			setRect(x, y, width, height);
			gameWorldRect = new Rectangle(0, 0, RE.worldWidth, RE.worldHeight );
			movementBoundsRect = gameWorldRect;
			bMovementBounds = true;
			aRenderLayers = [];
		}
		
		public function objOnCamera(obj:I2d):void {
			tempObj = obj;
			indexOfObj = -1;
			layerNum = tempObj.layer;
			
			// if obj is null
			if ( tempObj.isNull() ) {
				if (!tempObj.parallaxing) {
					if (aRenderLayers[layerNum]) {
						indexOfObj = aRenderLayers[layerNum].indexOf(tempObj);
						if (indexOfObj > -1) {
							aRenderLayers[layerNum].splice(indexOfObj, 1);
						}
					}
				}
				tempObj.visible = false;
				return;
			}
			
			// checks bounds of obj to see if it is on the camera
			tempRect = tempObj.rect;
			if (rect.right < tempRect.left || rect.left > tempRect.right ||
				rect.top > tempRect.bottom || rect.bottom < tempRect.top ) {
					bObjOnCamera = false;
				} else {
					bObjOnCamera = true;
			}
			
			// sets whether the obj renders
			tempObj.visible = bObjOnCamera;
			
			// checks for a parallaxing asset
			if (tempObj.parallaxing) { return; }
			else if (tempObj.backgroundObj) { return; }
			else if (tempObj.overlaying) { return; }
			
			// makes a new layer if it isn't made yet
			if (aRenderLayers[layerNum] == null) {
				aRenderLayers[layerNum] = new Vector.<I2d>;
				indexOfObj = -1;
			} else {
				indexOfObj = aRenderLayers[layerNum].indexOf(tempObj);
			}
			
			// adds or removes the asset to the render layer
			if (indexOfObj > -1) {
				if (!bObjOnCamera) {
					aRenderLayers[layerNum].splice(indexOfObj, 1);
				}
			} else {
				if (bObjOnCamera) {
					aRenderLayers[layerNum].push(tempObj);
				}
			}
		}
		
		override public function update():void {
			dx = 0;
			dy = 0;
			
			if (bOffset) {
				dx += offsetPoint.x;
				dy += offsetPoint.y;
			}
			
			if (bShake) {
				//recenters camera
				x -= shakeX;
				y -= shakeY;
				//update shake
				dx = shakeX = ( RLERandom.randomIntMinMax(nShakeAmnt, 1, true) * RLERandom.randomPosOrNeg() );
				dy = shakeY = ( RLERandom.randomIntMinMax(nShakeAmnt, 1, true) * RLERandom.randomPosOrNeg() );
			}
			
			if (bMove) {
				
				dx += _movePoint.x;
				dy += _movePoint.y;
				
				
			/*	if (bUpdateFromMouse) {
					updateRect.x = stage.mouseX;
					updateRect.y = stage.mouseY;
				} else if (bUpdateFromObj) {
					updateRect = updateObj.rect;
				}
				
				var num:Number = updateRect.left - cameraIdleRect.left;
				if (num < 0) {
					dx += num;
				} else {
					num = cameraIdleRect.right - updateRect.right;
					if (num < 0) {
						dx += num;
					}
				}
				
				num = updateRect.top - cameraIdleRect.bottom;
				if (num < 0) {
					dy += num;
				} else {
					num = updateRect.bottom - cameraIdleRect.top;
					if (num < 0) {
						dy += num;
					}
				}*/
			}
			
			super.x = dx;
			super.y = dy;
			
			if (bMovementBounds) {
				if (x > movementBoundsRect.right) { super.x = movementBoundsRect.right; }
				if (y > movementBoundsRect.bottom) { super.y = movementBoundsRect.bottom; }
				if (x < movementBoundsRect.left) { super.x = movementBoundsRect.left; }
				if (y < movementBoundsRect.top) { super.y = movementBoundsRect.top; }
			}
			//trace("Camera X: " + x);
			//trace("Camera Y: " + y);
		}
		
		public function setMovementBounds(top:Number, bottom:Number, right:Number, left:Number):void {
			if (movementBoundsRect == null) { movementBoundsRect = new Rectangle; }
			
			movementBoundsRect.top = top;
			movementBoundsRect.bottom = bottom;
			movementBoundsRect.right = right;
			movementBoundsRect.left = left;
		}
		
		public function enableMovementBounds(bool:Boolean):void {
			bMovementBounds = bool;
		}
		
		public function shake(pixels:int, frames:int):void {
			nShakeAmnt = pixels;
			addTickTimer("shake", frames);
			bShake = true;
		}
		
		override public function executeTimer(id:String):void {
			if (id == "shake") {
				nShakeAmnt = 0;
				bShake = false;
			}
		}
		
		override public function render():void {
			if (bParallax) {
				_parallax.render();
			}
		}
		
		public function followObject(obj:I2d):void {
			bUpdateFromObj = true;
			bUpdateFromMouse = false;
			this.updateObj = obj;
		}
		
		public function followMouse(bool:Boolean):void {
			bUpdateFromObj = bool;
			bUpdateFromObj = false;
			updateRect = new Rectangle(0,0,1,1);
		}
		
		public function set move(bool:Boolean):void {
			bMove = bool;
		}
		
		public function get move():Boolean {
			return bMove;
		}
		
		override public function set x(num:Number):void {
			_movePoint.x = num;
		}
		
		override public function set y(num:Number):void {
			_movePoint.y = num;
		}
		
		public function set offSet(point:Point):void {
			offsetPoint = point;
		}
		
		public function get offset():Point {
			return offsetPoint;
		}
		
		public function shouldItOffset(bool:Boolean):void {
			bOffset = bool;
		}
		
		public function get renderLayers():Array {
			return aRenderLayers;
		}
		
		public function set speed(point:Point):void {
			speedPoint = point;
		}
		
		public function get speed():Point {
			return speedPoint;
		}

		public function set parallax(para:RLEParallax):void {
			if (_parallax == para) { return; }
			bParallax = true;
			_parallax = para;
		}
				
		public function get parallax():RLEParallax { 
			return _parallax;
		}
		
		public function removeParallax():void {
			bParallax = false;
			_parallax.dispose();
			_parallax = null;
		}
		
		override public function dispose():void {
			speedPoint = null;
			updateObj = null;
			updateRect = null;
			offsetPoint = null;
			removeParallax();
			super.dispose();
		}
	}
}